package br.com.hs.nfe.common.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

/**
 * Classe responsável por tratar números.
 * @author ranlive
 */
public class NumberHelper {

    private NumberFormat numberFormat;

    /**
     * Construtor da classe.
     * @param locale Formato local.
     * @param groupingUsed Usar separador de números.
     * @param currencySymbol Formato moeda.
     */
    public NumberHelper(Locale locale, boolean groupingUsed, boolean currencySymbol) {
        if (currencySymbol) {
            numberFormat = NumberFormat.getCurrencyInstance(locale);
        } else {
            numberFormat = NumberFormat.getInstance(locale);
        }
        numberFormat.setGroupingUsed(groupingUsed);
    }

    /**
     * Contrutor da classe com tipos pré-definidos.
     * @param type Tipo enum com os tipos pré-definidos.
     */
    public NumberHelper(PreDefinedNumberFormatEnum type) {
        switch (type.ordinal()) {
            case 0:
                numberFormat = NumberFormat.getCurrencyInstance(new Locale("pt", "BR"));
                numberFormat.setGroupingUsed(true);
                break;
            case 1:
                numberFormat = NumberFormat.getCurrencyInstance(new Locale("pt", "BR"));
                numberFormat.setGroupingUsed(false);
                break;
            case 2:
                numberFormat = NumberFormat.getInstance(new Locale("pt", "BR"));
                numberFormat.setGroupingUsed(true);
                break;
            case 3:
                numberFormat = NumberFormat.getInstance(new Locale("pt", "BR"));
                numberFormat.setGroupingUsed(false);
                break;
            case 4:
                numberFormat = NumberFormat.getCurrencyInstance(new Locale("en", "US"));
                numberFormat.setGroupingUsed(true);
                break;
            case 5:
                numberFormat = NumberFormat.getCurrencyInstance(new Locale("en", "US"));
                numberFormat.setGroupingUsed(false);
                break;
            case 6:
                numberFormat = NumberFormat.getInstance(new Locale("en", "US"));
                numberFormat.setGroupingUsed(true);
                break;
            case 7:
                numberFormat = NumberFormat.getInstance(new Locale("en", "US"));
                numberFormat.setGroupingUsed(false);
                break;
        }
    }

    /**
     * Retorna um BigDecimal com o resultado da soma.
     * @param base Número base a somar.
     * @param valorString Array de valores que serão somados.
     * @return BigDecimal com o resultado da soma.
     */
    public BigDecimal somar(BigDecimal base, String[] valorString) {
        if (!StringHelper.isBlankOrNull(valorString)) {
            for (int i = 0; i < valorString.length; i++) {
                if (!StringHelper.isBlankOrNull(valorString[i])) {
                    base = base.add(getBigDecimal(valorString[i]));
                }
            }
        }
        return base;
    }

    /**
     * Retorna um BigDecimal com o resultado da subtração.
     * @param base Número base a subtrair.
     * @param valorString Array de valores que serão subtraidos.
     * @return BigDecimal com o resultado da subtração.
     */
    public BigDecimal subtrair(BigDecimal base, String[] valorString) {
        if (!StringHelper.isBlankOrNull(valorString)) {
            for (int i = 0; i < valorString.length; i++) {
                if (!StringHelper.isBlankOrNull(valorString[i])) {
                    base = base.subtract(getBigDecimal(valorString[i]));
                }
            }
        }
        return base;
    }

    /**
     * Retorna uma String com o valor percentual (valor / 100).
     * @param str Valor.
     * @return Resutado.
     */
    public String getPercentual(String str) {
        if (!StringHelper.isBlankOrNull(str)) {
            BigDecimal valor = new BigDecimal(str);
            BigDecimal resultado = valor.divide(new BigDecimal(100));
            return resultado.toString();
        }
        return null;
    }

    /**
     * Retorna o resultado de uma multiplicação ou null se ocorrer algum erro.
     * @param baseStr Número base.
     * @param multiplicandoStr Multiplicando.
     * @return String com o resultado.
     */
    public String multiplicar(String baseStr, String multiplicandoStr) {
        return multiplicar(baseStr, multiplicandoStr, RoundingMode.DOWN);
    }

    /**
     * Retorna o resultado de uma multiplicação ou null se ocorrer algum erro.
     * @param baseStr Número base.
     * @param multiplicandoStr Multiplicando.
     * @param roundingMode Modo de arredondamento.
     * @return String com o resultado.
     */
    public String multiplicar(String baseStr, String multiplicandoStr, RoundingMode roundingMode) {
        if ((StringHelper.isBlankOrNull(baseStr)) || (StringHelper.isBlankOrNull(multiplicandoStr))) {
            return null;
        }
        BigDecimal base = getBigDecimal(baseStr);
        BigDecimal multiplicando = getBigDecimal(multiplicandoStr);
        BigDecimal resultado = base.multiply(multiplicando).setScale(2, roundingMode);
        return numberFormat.format(resultado.doubleValue());
    }

    /**
     * Retorna um BigDecimal por uma String ou null se ocorrer algum erro.
     * @param valor Valor numérico.
     * @return BigDecimal do valor.
     */
    public BigDecimal getBigDecimal(String valor) {
        if (!StringHelper.isBlankOrNull(valor)) {
            try {
                if (numberFormat instanceof DecimalFormat) {
                    NumberFormat clone = (NumberFormat) numberFormat.clone();
                    ((DecimalFormat) clone).setParseBigDecimal(true);
                    return (BigDecimal) clone.parse(valor);
                }
                return new BigDecimal(numberFormat.parse(valor).toString());
            } catch (ParseException e) {
            }
        }
        return null;
    }

    /**
     * Retorna uma String formatada de um valor numérico.
     * @param valor Valor numérico.
     * @param maxIntegerDigits  Máximo de digitos a esquerda.
     * @param maxDecDigits Máximo de digitos a direita.
     * @return
     */
    public String toString(Number valor, int maxIntegerDigits, int maxDecDigits) {
        return toString(valor, 1, maxIntegerDigits, maxDecDigits, maxDecDigits);
    }

    /**
     * Retorna uma String formatada de um valor numérico.
     * @param valor Valor numérico.
     * @param minIntegerDigits Mínimo de digitos a esquerda.
     * @param maxIntDigits Máximo de digitos a esquerda.
     * @param minDecDigits Mínimo de digitos a direita.
     * @param maxDecDigits Máximo de digitos a direita.
     * @return
     */
    public String toString(Number valor, int minIntegerDigits, int maxIntDigits, int minDecDigits, int maxDecDigits) {
        if (valor == null) {
            return null;
        }
        NumberFormat clone = (NumberFormat) numberFormat.clone();
        clone.setRoundingMode(RoundingMode.DOWN);
        if (minDecDigits >= 0) {
            clone.setMinimumFractionDigits(minDecDigits);
        }
        if (maxDecDigits >= 0) {
            clone.setMaximumFractionDigits(maxDecDigits);
        }
        if (minIntegerDigits >= 0) {
            clone.setMinimumIntegerDigits(minIntegerDigits);
        }
        if (maxIntDigits >= 0) {
            clone.setMaximumIntegerDigits(maxIntDigits);
        }
        return clone.format(valor);
    }

    /**
     * 
     */
    public static enum PreDefinedNumberFormatEnum {

        /**
         * Locale PT-BR com formato moeda e separador.
         */
        PT_BR_GROUPING_CURRENCY_SYMBOL,
        /**
         * Locale PT-BR com separador.
         */
        PT_BR_GROUPING,
        /**
         * Locale PT-BR com formato moeda.
         */
        PT_BR_CURRENCY_SYMBOL,
        /**
         * Locale PT-BR com formato padrão.
         */
        PT_BR,
        /**
         * Locale EN-US com formato moeda e separador.
         */
        EN_US_GROUPING_CURRENCY_SYMBOL,
        /**
         * Locale EN-US com separador.
         */
        EN_US_GROUPING,
        /**
         * Locale EN-US com formato moeda.
         */
        EN_US_CURRENCY_SYMBOL,
        /**
         *  Locale EN-US com formato padrão.
         */
        EN_US;
    }
}
